/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#ifndef __MATH_QUATERNION_H__
#define __MATH_QUATERNION_H__


extern odQuat		quat_identity;

class odQuat {
public:
	float			x;
	float			y;
	float			z;
	float			w;

					odQuat (void);
	explicit		odQuat (float x, float y, float z);
	explicit		odQuat (float x, float y, float z, float w);

	float			operator [] (int index) const;
	float &			operator [] (int index);

	odQuat &		operator = (const odQuat &quat);
	bool			operator == (const odQuat &quat) const;
	bool			operator != (const odQuat &quat) const;

	odQuat			operator - () const;

	odQuat			operator + (const odQuat &quat) const;
	odQuat			operator - (const odQuat &quat) const;
	odQuat			operator * (const odQuat &quat) const;
	odVec3			operator * (const odVec3 &vec) const;
	odQuat			operator * (float scale) const;
	odQuat &		operator += (const odQuat &quat);
	odQuat &		operator -= (const odQuat &quat);
	odQuat &		operator *= (const odQuat &quat);
	odQuat &		operator *= (float scale);

	void			Set (float x, float y, float z);
	void			Set (float x, float y, float z, float w);
	void			CalcW (void);
	void			Identity (void);
	bool			IsIdentity (void) const;
	void			SLerp (const odQuat &from, const odQuat &to, float frac);
	float			Length (void) const;
	float			Normalize (void);

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	odMat3			ToMat3 (void) const;
	const char *	ToString (int precision = 2) const;
};

INLINE odQuat::odQuat (void){

}

INLINE odQuat::odQuat (float x, float y, float z){

	this->x = x;
	this->y = y;
	this->z = z;
	this->w = 1.0f;
}

INLINE odQuat::odQuat (float x, float y, float z, float w){

	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

INLINE float odQuat::operator [] (int index) const {

	return (&x)[index];
}

INLINE float &odQuat::operator [] (int index){

	return (&x)[index];
}

INLINE odQuat &odQuat::operator = (const odQuat &quat){

	x = quat.x;
	y = quat.y;
	z = quat.z;
	w = quat.w;

	return *this;
}

INLINE bool odQuat::operator == (const odQuat &quat) const {

	if (x != quat.x || y != quat.y || z != quat.z || w != quat.w)
		return false;

	return true;
}

INLINE bool odQuat::operator != (const odQuat &quat) const {

	if (x == quat.x && y == quat.y && z == quat.z && w == quat.w)
		return false;

	return true;
}

INLINE odQuat odQuat::operator - () const {

	return odQuat(-x, -y, -z, -w);
}

INLINE odQuat odQuat::operator + (const odQuat &quat) const {

	return odQuat(x + quat.x, y + quat.y, z + quat.z, w + quat.w);
}

INLINE odQuat odQuat::operator - (const odQuat &quat) const {

	return odQuat(x - quat.x, y - quat.y, z - quat.z, w - quat.w);
}

INLINE odQuat odQuat::operator * (const odQuat &quat) const {

	odQuat	result;

	result.x = w * quat.x + x * quat.w + y * quat.z - z * quat.x;
	result.y = w * quat.y + y * quat.w + z * quat.x - x * quat.z;
	result.z = w * quat.z + z * quat.w + x * quat.y - y * quat.x;
	result.w = w * quat.w - x * quat.x - y * quat.y - z * quat.z;

	return result;
}

INLINE odVec3 odQuat::operator * (const odVec3 &vec) const {

	odVec3	result;
	float	xx, yy, zz;
	float	xy, xz, yz;
	float	wx, wy, wz;
	float	x2, y2, z2;

	x2 = x + x;
	y2 = y + y;
	z2 = z + z;

	xx = x * x2;
	yy = y * y2;
	zz = z * z2;

	xy = x * y2;
	xz = x * z2;
	yz = y * z2;

	wx = w * x2;
	wy = w * y2;
	wz = w * z2;

	result.x = (1.0f - yy - zz) * vec.x + (xy - wz) * vec.y + (xz + wy) * vec.z;
	result.y = (xy + wz) * vec.x + (1.0f - xx - zz) * vec.y + (yz - wx) * vec.z;
	result.z = (xz - wy) * vec.x + (yz + wx) * vec.y + (1.0f - xx - yy) * vec.z;

	return result;
}

INLINE odQuat odQuat::operator * (float scale) const {

	return odQuat(x * scale, y * scale, z * scale, w * scale);
}

INLINE odQuat &odQuat::operator += (const odQuat &quat){

	x += quat.x;
	y += quat.y;
	z += quat.z;
	w += quat.w;

	return *this;
}

INLINE odQuat &odQuat::operator -= (const odQuat &quat){

	x -= quat.x;
	y -= quat.y;
	z -= quat.z;
	w -= quat.w;

	return *this;
}

INLINE odQuat &odQuat::operator *= (const odQuat &quat){

	float	qx, qy, qz, qw;

	qx = x;
	qy = y;
	qz = z;
	qw = w;

	x = qw * quat.x + qx * quat.w + qy * quat.z - qz * quat.x;
	y = qw * quat.y + qy * quat.w + qz * quat.x - qx * quat.z;
	z = qw * quat.z + qz * quat.w + qx * quat.y - qy * quat.x;
	w = qw * quat.w - qx * quat.x - qy * quat.y - qz * quat.z;

	return *this;
}

INLINE odQuat &odQuat::operator *= (float scale){

	x *= scale;
	y *= scale;
	z *= scale;
	w *= scale;

	return *this;
}

INLINE void odQuat::Set (float x, float y, float z){

	this->x = x;
	this->y = y;
	this->z = z;
	this->w = 1.0f;
}

INLINE void odQuat::Set (float x, float y, float z, float w){

	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

INLINE void odQuat::CalcW (void){

	float	qw;

	qw = 1.0f - (x*x + y*y + z*z);

	if (qw < 0.0f)
		w = 0.0f;
	else
		w = -M_Sqrt(qw);
}

INLINE void odQuat::Identity (void){

	x = 0.0f;
	y = 0.0f;
	z = 0.0f;
	w = 1.0f;
}

INLINE bool odQuat::IsIdentity (void) const {

	if (x != 0.0f || y != 0.0f || z != 0.0f || w != 1.0f)
		return false;

	return true;
}

INLINE float odQuat::Length (void) const {

	return M_Sqrt(x*x + y*y + z*z + w*w);
}

INLINE float odQuat::Normalize (void){

	float	length, invLength;

	length = M_Sqrt(x*x + y*y + z*z + w*w);

	if (length){
		invLength = 1.0f / length;

		x *= invLength;
		y *= invLength;
		z *= invLength;
		w *= invLength;
	}

	return length;
}

INLINE const float *odQuat::ToFloatPtr (void) const {

	return &x;
}

INLINE float *odQuat::ToFloatPtr (void){

	return &x;
}


#endif	// __MATH_QUATERNION_H__
